package cn.keniykey.mc.viewlibrary.packet;

import java.util.ArrayList;
import java.util.HashMap;

import javax.annotation.Nullable;

import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import cn.keniykey.mc.viewlibrary.channel.AbChannelUtil;
import cn.keniykey.mc.viewlibrary.container.api.EContainerType;
import cn.keniykey.mc.viewlibrary.packet.api.AbPacket;
import cn.keniykey.mc.viewlibrary.packet.api.IPacketDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketItemName;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowClick;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowClose;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowCloseDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowDataDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowItemsDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowOpenDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowSetSlotDispatch;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionClass;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionConstructor;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionField;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionMethod;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionUtil;
import cn.keniykey.mc.viewlibrary.reflection.SearchReflectionMethod;

public final class PacketUtilV1_16_3 extends AbPacketUtil{
	private final ReflectionMethod method_bukkit_itemstack_asnmscopy,method_bukkit_craftchatmessage_fromstring;
	private final ReflectionConstructor constructor_nms_chatcomponenttext;
	private final Object obj_nms_itemstack_emptyitem;
	
	private final ReflectionConstructor constructor_nms_packet_out_closewindow,constructor_nms_packet_out_openwindow;
	private final ReflectionConstructor constructor_nms_packet_out_windowsetslot,constructor_nms_packet_out_windowitems;
	private final ReflectionConstructor constructor_nms_packet_out_windowdata;
	private final ReflectionConstructor constructor_nms_nonnulllist;
	
	private final HashMap<EContainerType,Object> hashmap_containers;
	
	private final ReflectionClass class_nms_packet;
	private final ReflectionClass class_nms_packet_in_closewindow,class_nms_packet_in_clickwindow;
	private final ReflectionClass class_nms_packet_in_itemname;
	
	private final ReflectionField field_nms_packet_in_closewindow_id;
	//PacketPlayInWindowClick中a表示id
	private final ReflectionField field_nms_packet_in_clickwindow_id,field_nms_packet_in_clickwindow_slot;
	private final ReflectionField field_nms_packet_in_clickwindow_button,field_nms_packet_in_clickwindow_shift;
	private final ReflectionField field_nms_packet_in_itemname_name;
	
	PacketUtilV1_16_3() {
		this.method_bukkit_itemstack_asnmscopy = ReflectionUtil.findReflectionClass("cb,inventory.CraftItemStack").findMethodByName("asNMSCopy");
		this.obj_nms_itemstack_emptyitem = this.method_bukkit_itemstack_asnmscopy.invoke(new ItemStack(Material.AIR));
		this.method_bukkit_craftchatmessage_fromstring = ReflectionUtil.findReflectionClass("cb,util.CraftChatMessage").findMethod(
				new SearchReflectionMethod().setName("fromString").addParameter(String.class));
		this.constructor_nms_chatcomponenttext = ReflectionUtil.findReflectionClass("nms,ChatComponentText").getConstructor(String.class);
		this.constructor_nms_packet_out_closewindow = ReflectionUtil.findReflectionClass("nms,PacketPlayOutCloseWindow").getConstructor(int.class);
		this.constructor_nms_packet_out_openwindow = ReflectionUtil.findReflectionClass("nms,PacketPlayOutOpenWindow").findConstructor(3);
		
		final ReflectionClass class_nms_containers = ReflectionUtil.findReflectionClass("nms,Containers");
		final ArrayList<ReflectionField> methods_nms_containers = class_nms_containers.findFields(class_nms_containers);
		this.hashmap_containers = new HashMap<>();
		try {
			for(final ReflectionField field : methods_nms_containers) {
				final EContainerType type = EContainerType.coverEContainerType(field.getField().getName());
				if(type != null) this.hashmap_containers.put(type, field.get());
			}
		}finally {
			methods_nms_containers.clear();
		}
		this.constructor_nms_packet_out_windowsetslot = ReflectionUtil.findReflectionClass("nms,PacketPlayOutSetSlot").findConstructor(3);
		this.constructor_nms_packet_out_windowitems = ReflectionUtil.findReflectionClass("nms,PacketPlayOutWindowItems").findConstructor(2);
		this.constructor_nms_packet_out_windowdata = ReflectionUtil.findReflectionClass("nms,PacketPlayOutWindowData").findConstructor(3);
		this.constructor_nms_nonnulllist = ReflectionUtil.findReflectionClass("nms,NonNullList").findConstructor(2);
		
		this.class_nms_packet = ReflectionUtil.findReflectionClass("nms,Packet");
		this.class_nms_packet_in_closewindow = ReflectionUtil.findReflectionClass("nms,PacketPlayInCloseWindow");
		this.field_nms_packet_in_closewindow_id = this.class_nms_packet_in_closewindow.findFieldByName("id");
		this.class_nms_packet_in_clickwindow = ReflectionUtil.findReflectionClass("nms,PacketPlayInWindowClick");
		this.field_nms_packet_in_clickwindow_id = this.class_nms_packet_in_clickwindow.findFieldByName("a");
		this.field_nms_packet_in_clickwindow_slot = this.class_nms_packet_in_clickwindow.findFieldByName("slot");
		this.field_nms_packet_in_clickwindow_button = this.class_nms_packet_in_clickwindow.findFieldByName("button");
		this.field_nms_packet_in_clickwindow_shift = this.class_nms_packet_in_clickwindow.findFieldByName("shift");
		this.class_nms_packet_in_itemname = ReflectionUtil.findReflectionClass("nms,PacketPlayInItemName");
		this.field_nms_packet_in_itemname_name = this.class_nms_packet_in_itemname.findField(String.class);
	}
	@Override
	public void dispatchPacket(Player player, IPacketDispatch packet, boolean cache) {
		//注意！packet里面的sendPacket靠这个方法实现，不要套娃！
		Object nmspacket = packet.getCacheReflectionPacket();
		if(nmspacket == null || !this.class_nms_packet.isInstance(nmspacket)) {
			if(packet instanceof PacketWindowCloseDispatch) {
				final PacketWindowCloseDispatch pack = (PacketWindowCloseDispatch)packet;
				nmspacket = this.constructor_nms_packet_out_closewindow.newInstance(pack.getID());
			}else if(packet instanceof PacketWindowItemsDispatch) {
				final PacketWindowItemsDispatch pack = (PacketWindowItemsDispatch)packet;
				final ArrayList<Object> nmsitems = new ArrayList<>(pack.getItems().size());
				for(final ItemStack item : pack.getItems()) nmsitems.add(this.toNmsItemStack(item)); //toNmsItemStack已实现判空添加空气物品
				final Object nmslist = this.constructor_nms_nonnulllist.newInstance(nmsitems, this.obj_nms_itemstack_emptyitem);
				nmspacket = this.constructor_nms_packet_out_windowitems.newInstance(pack.getID(), nmslist);
			}else if(packet instanceof PacketWindowOpenDispatch) {		
				final PacketWindowOpenDispatch pack = (PacketWindowOpenDispatch)packet;
				if(!this.hashmap_containers.containsKey(pack.getContainerType())) {
					throw new RuntimeException("Unknown EContainerType(在当前版本属于未知界面类型)");
				}
				nmspacket = this.constructor_nms_packet_out_openwindow.newInstance(pack.getID(),
				this.hashmap_containers.get(pack.getContainerType()),this.toChatBaseComponent(pack.getTitle(), false));		
			}else if(packet instanceof PacketWindowSetSlotDispatch) {
				final PacketWindowSetSlotDispatch pack = (PacketWindowSetSlotDispatch)packet;
				nmspacket = this.constructor_nms_packet_out_windowsetslot.newInstance(pack.getID(),pack.getSlot(),
				this.toNmsItemStack(pack.getItemStack()));
			}else if(packet instanceof PacketWindowDataDispatch) {
//				throw new RuntimeException("Deprecated packet dispatch in this version(在当前版本属于无效发包类型)");
				final PacketWindowDataDispatch pack = (PacketWindowDataDispatch)packet;
				nmspacket = this.constructor_nms_packet_out_windowdata.newInstance(pack.getID(),pack.getProperty(),pack.getValue());
			}else {
				throw new RuntimeException("Unknown packet dispatch(未知发包类型)");
			}
			if(cache) packet.setCacheReflectionPacket(nmspacket);
		}
		AbChannelUtil.getUtil().sendPacket(player, nmspacket);
	}
	private Object toChatBaseComponent(String str, boolean craft) {
		if(!craft) return this.constructor_nms_chatcomponenttext.newInstance(str);
		final Object[] objs = (Object[]) this.method_bukkit_craftchatmessage_fromstring.invoke(str);
		return objs[0];
	}
	private Object toNmsItemStack(@Nullable ItemStack item) {
		if(item == null || item.getType().equals(Material.AIR)) return this.obj_nms_itemstack_emptyitem;
		return this.method_bukkit_itemstack_asnmscopy.invoke(item);
	}
	@Nullable
	@Override
	public AbPacket coverPacket(Object nmspacket, int windowid) {
		if(this.class_nms_packet_in_closewindow.isInstance(nmspacket)) {
			final int id = (int) this.field_nms_packet_in_closewindow_id.dynamicGet(nmspacket);
			if(windowid != id) return null;
			return new PacketWindowClose(id);
		}
		if(this.class_nms_packet_in_clickwindow.isInstance(nmspacket)) {
			final int id = (int) this.field_nms_packet_in_clickwindow_id.dynamicGet(nmspacket);
			if(windowid != id) return null;
			final String mode = this.field_nms_packet_in_clickwindow_shift.dynamicGet(nmspacket).toString();
			final int button = (int) this.field_nms_packet_in_clickwindow_button.dynamicGet(nmspacket);
			final int slot = (int) this.field_nms_packet_in_clickwindow_slot.dynamicGet(nmspacket);
			return new PacketWindowClick(windowid, mode, button, slot);
		}
		if(this.class_nms_packet_in_itemname.isInstance(nmspacket)) {
			final String name = (String) this.field_nms_packet_in_itemname_name.dynamicGet(nmspacket);
			return new PacketItemName(name);
		}
		return null;
	}

}